package cn.jinbyte.web.filter;

import cn.jinbyte.web.config.ApiLogProperties;
import cn.jinbyte.web.wrapper.RepeatReadRequestWrapper;
import cn.jinbyte.web.wrapper.RepeatReadResponseWrapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.server.PathContainer;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

/**
 * 接口日志过滤器
 * <p>
 * 对需要记录的接口请求进行包装处理
 * </p
 *
 * @author jinty
 */
@RequiredArgsConstructor
public class ApiLogFilter extends OncePerRequestFilter {
    private final ApiLogProperties logProperties;

    @Override
    protected void doFilterInternal(@NotNull HttpServletRequest request,
                                    @NotNull HttpServletResponse response,
                                    @NotNull FilterChain filterChain)
            throws ServletException, IOException {
        if (isValidRequest(request) && !isExcludedRequest(request)) {
            if (isRequestWrapper(request)) {
                request = new RepeatReadRequestWrapper(request);
            }
            if (isResponseWrapper(response)) {
                response = new RepeatReadResponseWrapper(response);
            }
        }
        filterChain.doFilter(request, response);
        // 如果响应被包装了，复制缓存数据到原始响应
        if (response instanceof RepeatReadResponseWrapper wrappedResponse) {
            wrappedResponse.copyBodyToResponse();
        }
    }

    /**
     * 是否有效请求
     *
     * @param request 请求对象
     * @return true：是；false：否
     */
    private boolean isValidRequest(HttpServletRequest request) {
        try {
            new URI(request.getRequestURL().toString());
            return true;
        } catch (URISyntaxException e) {
            return false;
        }
    }

    /**
     * 是否排除请求
     *
     * @param request 请求对象
     * @return true：是；false：否
     */
    private boolean isExcludedRequest(HttpServletRequest request) {
        return logProperties.getExcludePatterns().stream()
                .anyMatch(pattern -> {
                    PathPattern pathPattern = PathPatternParser.defaultInstance.parse(pattern);
                    PathContainer pathContainer = PathContainer.parsePath(request.getRequestURI());
                    return pathPattern.matches(pathContainer);
                });
    }

    /**
     * 是否需要包装输入流
     *
     * @param request 请求对象
     * @return true：是；false：否
     */
    private boolean isRequestWrapper(HttpServletRequest request) {
        return !(request instanceof RepeatReadRequestWrapper);
    }


    /**
     * 是否需要包装输出流
     *
     * @param response 响应对象
     * @return true：是；false：否
     */
    private boolean isResponseWrapper(HttpServletResponse response) {
        return !(response instanceof RepeatReadResponseWrapper);
    }
}
